﻿using DotNetExtensions.Core.Autofac;
using DotNetExtensions.Core.MVC;
using DotNetExtensions.Core.WebApi.Filters;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace DotNetExtensions.Core.WebApi.Jwt
{
    public static class JwtExtensions
    {
        public static void AddJwt<T>(this IServiceCollection services, JwtOption option) where T : class, new()
        {
            services.AddAuthorization();
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters()
                {
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero,
                    ValidateAudience = true,
                    AudienceValidator = (m, n, z) => AudienceValidator<T>(m, n, z, option),
                    ValidateIssuer = true,
                    ValidIssuer = option.Issuer,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(option.SecurityKeyBuffer)
                };
                options.Events = new JwtBearerEvents() { OnTokenValidated = OnTokenValidated, OnAuthenticationFailed = AuthenticationFailed };
            });
        }
        static async Task AuthenticationFailed(AuthenticationFailedContext context)
        {
            await Task.CompletedTask;
        }
        static bool AudienceValidator<T>(IEnumerable<string> audiences, SecurityToken securityToken, TokenValidationParameters validationParameters, JwtOption option) where T : class, new()
        {
            bool isValid = false;
            if (securityToken is JwtSecurityToken jwtSecurityToken)
            {
                JwtParameters parameters = new JwtParameters();

                foreach (var item in jwtSecurityToken.Claims)
                {
                    JwtParameter jwt = new JwtParameter(item.Type, item.Value);
                    parameters.Add(jwt);
                }

                if (audiences != null && audiences.Any())
                {
                    var validateService = RegistTypeResolver.Resolve<IJwtValidateService<T>>();
                    bool valid = validateService.Validate(audiences.FirstOrDefault(), parameters, out T user, out string refreshKey);
                    jwtSecurityToken.Header.Add("RefreshKey", refreshKey);
                    return valid;
                }
            }

            return isValid;
        }

        static async Task OnTokenValidated(TokenValidatedContext context)
        {
            if ((context.SecurityToken is JwtSecurityToken jwtSecurityToken) && jwtSecurityToken.Header.TryGetValue("RefreshKey", out object refreshKey))
            {
                context.HttpContext.Response.Headers.Add("refresh-token", refreshKey.ToString());
                context.HttpContext.Response.Headers.Add("access-token", jwtSecurityToken.RawData);
            }
            await Task.CompletedTask;
        }
    }
}
